package bma.common.langutil.ai.common;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import bma.common.langutil.ai.stack.AIStack;
import bma.common.langutil.ai.stack.AIStackAbstract;
import bma.common.langutil.core.Function;
import bma.common.langutil.core.TransformList;

/**
 * 并行执行的控制器
 * 
 * @author guanzhong
 * @since 1.0 (imported from BMA.ESP Project)
 * 
 * @param <CTX_TYPE>
 * @param <IN_TYPE>
 * @param <RESP_TYPE>
 */
public abstract class AIGroupAll<CTX_TYPE, IN_TYPE, RESP_TYPE> {

	public class GroupResult {
		public IN_TYPE value;
		public CTX_TYPE context;
	};

	protected AIStack<RESP_TYPE> stack;
	protected List<GroupResult> results;
	protected AtomicInteger resultCount;
	protected boolean ingoreFail;
	protected int count;

	public AIGroupAll(AIStack<RESP_TYPE> stack, int count) {
		this(stack, count, false);
	}

	public AIGroupAll(AIStack<RESP_TYPE> stack, int count, boolean ingoreFail) {
		super();
		this.count = count;
		this.stack = stack;
		this.ingoreFail = ingoreFail;
		resultCount = new AtomicInteger(count);
		this.results = Collections.synchronizedList(new ArrayList<GroupResult>(
				count));
	}

	public <F_TYPE> AIStack<F_TYPE> newStack(final CTX_TYPE ctx,
			final Function<F_TYPE, IN_TYPE> tranform) {
		AIStack<F_TYPE> newStack = new AIStackAbstract<F_TYPE>() {

			AtomicBoolean once = new AtomicBoolean();

			@Override
			public AIStack<?> getParent() {
				return stack;
			}

			@Override
			public boolean failure(Throwable t) {
				if (once.getAndSet(true))
					return true;
				if (ingoreFail) {
					doneOne();
					return true;
				}
				return stack.failure(t);
			}

			@Override
			public boolean success(F_TYPE result) {
				if (once.getAndSet(true))
					return true;
				doneOne(ctx, tranform.apply(result));
				return true;
			}
		};
		return newStack;
	}

	public boolean isIngoreFail() {
		return ingoreFail;
	}

	public void setIngoreFail(boolean ingoreFail) {
		this.ingoreFail = ingoreFail;
	}

	public AIStack<IN_TYPE> newStack(final CTX_TYPE ctx) {
		return new AIStackAbstract<IN_TYPE>() {
			@Override
			public AIStack<?> getParent() {
				return stack;
			}

			@Override
			public boolean failure(Throwable t) {
				if (ingoreFail) {
					doneOne();
					return true;
				}
				return stack.failure(t);
			}

			@Override
			public boolean success(IN_TYPE result) {
				doneOne(ctx, result);
				return true;
			}
		};
	}

	public boolean commit() {
		if (count == 0) {
			return stack.success(convertResult(results));
		}
		return resultCount.get() == 0;
	}

	protected void doneOne() {
		if (resultCount.decrementAndGet() == 0) {
			stack.success(convertResult(results));
		}
	}

	protected void doneOne(CTX_TYPE ctx, IN_TYPE result) {
		GroupResult r = new GroupResult();
		r.value = result;
		r.context = ctx;
		this.results.add(r);
		doneOne();
	}

	protected abstract RESP_TYPE convertResult(List<GroupResult> results);

	public static abstract class Simple<IN_T, R_T> extends
			AIGroupAll<Object, IN_T, R_T> {

		public Simple(AIStack<R_T> stack, int count, boolean ingoreFail) {
			super(stack, count, ingoreFail);
		}

		public Simple(AIStack<R_T> stack, int count) {
			super(stack, count);
		}

		@Override
		protected R_T convertResult(List<GroupResult> results) {
			List<IN_T> lt = TransformList.transform(results,
					new Function<GroupResult, IN_T>() {
						@Override
						public IN_T apply(GroupResult input) {
							return input.value;
						};
					});
			return convert(lt);
		}

		protected abstract R_T convert(List<IN_T> results);

		public AIStack<IN_T> newStack() {
			return super.newStack(null);
		}

		public <F_TYPE> AIStack<F_TYPE> newStack(Function<F_TYPE, IN_T> tranform) {
			return super.newStack(null, tranform);
		}

	}

	public static class Collection<COLL_T> extends
			AIGroupAll.Simple<COLL_T, List<COLL_T>> {

		public Collection(AIStack<List<COLL_T>> stack, int count,
				boolean ingoreFail) {
			super(stack, count, ingoreFail);
		}

		public Collection(AIStack<List<COLL_T>> stack, int count) {
			super(stack, count);
		}

		@Override
		protected List<COLL_T> convert(List<COLL_T> results) {
			return results;
		}
	}

	public static <IN_T, RESP_T> AIGroupAll.Simple<IN_T, RESP_T> newGroup(
			AIStack<RESP_T> stack, int count,
			final Function<List<IN_T>, RESP_T> transform) {
		return new AIGroupAll.Simple<IN_T, RESP_T>(stack, count) {

			@Override
			protected RESP_T convert(List<IN_T> results) {
				return transform.apply(results);
			}
		};
	}

}
